package com.renhui.android.handwriting.eventbus;

import android.os.Handler;
import android.os.Looper;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class EventBus {

    private Handler handler;

    private ExecutorService executorService;

    private Map<Object, List<SubscribleMethod>> cacheMap;

    private static EventBus instance = new EventBus();

    public static EventBus getDefault() {
        return instance;
    }

    private EventBus() {
        this.cacheMap = new HashMap<>();
        this.handler = new Handler(Looper.getMainLooper());
        this.executorService = Executors.newCachedThreadPool();
    }

    // 注册方法
    public void register(Object subscriber) {
        Class<?> aClass = subscriber.getClass();
        // 尝试从缓存中拿数据
        List<SubscribleMethod> subscribleMethods = cacheMap.get(subscriber);
        // 如果已注册，就不需要再注册了，如果没有，就进行遍历注册
        if (subscribleMethods == null) {
            subscribleMethods = getSubscribleMethod(subscriber);
            cacheMap.put(subscriber, subscribleMethods);
        }
    }

    //  发送事件
    public void post(final Object object) {
        Set<Object> keySet = cacheMap.keySet();
        Iterator<Object> iterator = keySet.iterator();
        while (iterator.hasNext()) {
            // 拿到注册类
            Object next = iterator.next();
            // 获取所有添加注解的方法
            List<SubscribleMethod> list = cacheMap.get(next);
            for (SubscribleMethod subscribleMethod : list) {
                // 判断这个方法是否应该接受事件
                if (subscribleMethod.getEventType().isAssignableFrom(object.getClass())) {
                    switch (subscribleMethod.getThreadMode()) {
                        case MAIN: // 如果需要接受方法在主线程执行
                            if (Looper.myLooper() == Looper.getMainLooper()) {
                                invoke(subscribleMethod, next, object);
                            } else {
                                handler.post(() -> invoke(subscribleMethod, next, object));
                            }
                            break;
                        case ASYNC: // 如果需要接受方法在主线程执行
                            if (Looper.myLooper() == Looper.getMainLooper()) {
                                executorService.execute(() -> invoke(subscribleMethod, next, object));
                            } else {
                                invoke(subscribleMethod, next, object);
                            }
                            break;
                        case POSTING:
                            break;
                        case BACKGROUND:
                            break;
                        case MAIN_ORDERED:
                            break;
                    }

                }
            }
        }
    }

    private void invoke(SubscribleMethod subscribleMethod, Object next, Object object) {
        Method method = subscribleMethod.getMethod();
        try {
            // 第一个参数表示要调用哪个方法，第二个参数表示要传递的参数
            method.invoke(next, object);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    // 取消注册
    public void unRegister(Object subscriber) {
        Class<?> aClass = subscriber.getClass();
        List<SubscribleMethod> list = cacheMap.get(subscriber);
        if (list != null) {
            cacheMap.remove(subscriber);
        }
    }

    // 遍历能够接收事件的方法， 将subscriber这个类中所有的订阅方法添加到集合 并返回
    private List<SubscribleMethod> getSubscribleMethod(Object subscriber) {
        List<SubscribleMethod> list = new ArrayList<>();
        Class<?> aClass = subscriber.getClass();
        // 从当前类开始一层层的网上找：例如 subscriber --> BaseActivity --> Activity
        while (aClass != null) {
            // 判断类是在哪个包下（一般的，我们不检索系统API下的类）
            String name = aClass.getName();
            if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.") || name.startsWith("androidx.")) {
                break;
            }

            Method[] declaredMethod = aClass.getDeclaredMethods();

            for (Method method : declaredMethod) {
                Subscribe annotation = method.getAnnotation(Subscribe.class);
                if (annotation == null) {
                    continue;
                }
                // 获取参数类型 - EventBus 规定只允许一个参数
                Class<?>[] parameterTypes = method.getParameterTypes();
                // 判断是否符合要求
                if (parameterTypes.length != 1) {
                    throw new RuntimeException("EventBus只允许一个参数!");
                }
                // 符合要求
                ThreadMode threadMode = annotation.threadMode();

                SubscribleMethod subscribleMethod = new SubscribleMethod(method, threadMode, parameterTypes[0]);
                list.add(subscribleMethod);
            }
            aClass = aClass.getSuperclass();
        }
        return list;
    }

}
